iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0
Software Development

輕鬆學習設計模式Design Pattern系列 第 6

Day 6 單例模式 Singleton Pattern

  • 分享至 

  • xImage
  •  

在軟體開發中,有時我們需要確保某個類別在整個應用程式中只存在一個實例。例如,當我們開發一個設定管理器時,需要保證在整個程式執行期間,設定的讀取和修改都要經由同一個物件。這時候單例模式 Singleton Pattern 就派上用場了。

什麼是單例模式?

單例模式是一種建立型設計模式,它確保一個類別在整個程式中只有一個實例,並且提供一個全域性都能存取的方式。這個模式通常用於管理共享資源、設定管理、日誌系統等需要單一實例的情況。聽起來很簡單,但是它的應用卻無處不在。

單例模式在設定管理器的應用

讓我們透過一個設定管理器的範例來看看如何在 C++ 中實作單例模式,什麼餓漢與懶漢的今天不多做介紹,我就介紹一個多數情況馬上就能使用的方式,最簡單好用也是常見的實作方式。

首先我們定義一個 ConfigManager 類別,它負責管理應用程式中的設定。

class ConfigManager {
public:
    // 取得唯一實例的靜態方法
    static ConfigManager& getInstance() {
        static ConfigManager instance; // 唯一實例
        return instance;
    }

    // 禁止複製建構與賦值
    ConfigManager(const ConfigManager&) = delete;
    void operator=(const ConfigManager&) = delete;

    void setConfig(const std::string& key, const std::string& value) {
        configData[key] = value;
    }

    std::string getConfig(const std::string& key) const {
        if (configData.find(key) != configData.end()) {
            return configData.at(key);
        } else {
            return "Config not found!";
        }
    }

private:
    ConfigManager() {} // 禁止外部建立實例

    std::unordered_map<std::string, std::string> configData; // 儲存設定的容器
};

在這個範例中,getInstance 方法使用 static 關鍵字保證了 ConfigManager 類別只有一個實例被建立。建構子是 private 的,這樣就無法透過 new 或其他方式在外部建立新的實例。另外也禁用了複製建構子和賦值運算子,避免無意間複製實例。

假設我們在一個應用程式中需要管理一些全域性的設定,例如資料庫連線字串、連接埠等。我們可以使用 ConfigManager 來統一管理這些設定。

int main() {
    // 取得唯一的 ConfigManager 實例
    ConfigManager& config = ConfigManager::getInstance();

    std::cout << "Database host: " << config.getConfig("db_host") << std::endl;

    config.setConfig("db_host", "localhost");
    config.setConfig("db_port", "3306");

    std::cout << "Database host: " << config.getConfig("db_host") << std::endl;
    std::cout << "Database port: " << config.getConfig("db_port") << std::endl;

    // 嘗試在另一個地方取得 ConfigManager 實例
    ConfigManager& anotherConfig = ConfigManager::getInstance();
    std::cout << "Database host: " << anotherConfig.getConfig("db_host") << std::endl;
    return 0;
}

執行上述程式碼,我們會得到以下輸出:

Database host: Config not found!
Database host: localhost
Database port: 3306
Database host: localhost

這個範例中,我們先透過 getInstance() 取得唯一的 ConfigManager 實例,然後設定和取得不同的設定項目。無論程式的其他部分如何操作,都保證了設定管理器只有一個實例在執行。

以上我們介紹的是 Scott Meyers 在《Effective C++》中提出了一種簡潔的單例寫法,利用 local static object 在函式第一次被呼叫使用才初始化的特性,這寫法保證 C++11 以後是保證 thread-safe 的,另外也可以使用 STL 標準函式庫中的 std::call_once 來解決多執行緒建立單例這個問題,這邊不多贅述。

單例模式的優缺點

單例模式的優點如下:

  • 唯一性:確保應用程式中某個類別只有一個實例,方便管理全域性資源。
  • 方便存取:透過集中化管理,可以控制資源的存取和使用,避免多個實例帶來的混亂。
  • 延遲實例化:單例模式通常是延遲實例化的,只有在第一次使用時才會建立實例,有助於節省資源。

單例模式的缺點如下:

  • 全域狀態:單例模式會在整個程式裡建立一個大家都能用的共享狀態,可能導致程式難以測試和除錯,尤其是在多執行緒環境中。
  • 違反單一責任原則:單例模式不僅負責建立實例,還負責管理其狀態和行為,這可能導致類別職責過於龐大。

總結

單例模式在需要唯一實例的情境中是一種有效的設計模式,尤其是在設定管理器、日誌記錄器、資料庫連線、資源管理器等方面。透過本篇中的設定管理器範例,我們看到了如何在 C++ 中實作單例模式,以及這種模式的優缺點。在實際開發中,應該根據需求謹慎選擇是否使用單例模式,以平衡程式的設計和可維護性。

更多C++語言相關的文章,歡迎追蹤我的部落格,內有餓漢與懶漢的詳細介紹篇幅。
https://shengyu7697.github.io/cpp-singleton-pattern/


上一篇
Day 5 樣板方法模式 Template Method Pattern
下一篇
Day 7 工廠方法模式 Factory Method Pattern
系列文
輕鬆學習設計模式Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言